package com.college.common.utils;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import okhttp3.FormBody.Builder;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.hc.client5.http.classic.methods.HttpPost;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.entity.mime.ContentBody;
import org.apache.hc.client5.http.entity.mime.MultipartEntityBuilder;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.core5.http.HttpEntity;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.util.Timeout;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

@Slf4j
public class HttpUtil {
    private static final String CONTENT_TYPE = "application/json; charset=utf-8";
    private static final String FILE_CONTENT_TYPE = "application/octet-stream";
    private static final String ACCEPT = "application/json";
    private static final String FROM_DATA = "multipart/form-data";
    private static final MediaType MEDIA_TYPE_JSON = MediaType.parse(CONTENT_TYPE);
    private static final MediaType MEDIA_TYPE_FILE = MediaType.parse(FILE_CONTENT_TYPE);
    private static OkHttpClient client = null;

    static {
        int cacheSize = 10 * 1024 * 1024; // 10MB
        File cacheDirectory = null;
        try {
            cacheDirectory = Files.createTempDirectory("cache").toFile();
            Cache cache = new Cache(cacheDirectory, cacheSize);
            OkHttpClient.Builder httpBuilder = new OkHttpClient().newBuilder().sslSocketFactory(OkHttpUtil.getIgnoreInitedSslContext().getSocketFactory(), OkHttpUtil.IGNORE_SSL_TRUST_MANAGER_X509)
                    .hostnameVerifier(OkHttpUtil.getIgnoreSslHostnameVerifier()).build().newBuilder();
            client = httpBuilder.connectTimeout(10, TimeUnit.SECONDS).readTimeout(30, TimeUnit.SECONDS).writeTimeout(30, TimeUnit.SECONDS)
                    .cache(cache) // 设置超时时间
                    .build();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    /**
     * 同步get请求
     *
     * @param url
     * @return
     * @throws IOException
     * @
     */
    public static String syncGet(String url) throws IOException {
        Request request = new Request.Builder().url(url).get().build();
        String result = execute(request);
        return result;
    }

    /**
     * 同步get请求
     *
     * @param url
     * @return
     * @throws IOException
     * @
     */
    public static String syncGet(String url, Map<String, String> headers) {
        Request.Builder builder = new Request.Builder().url(url);
        if (headers != null && !headers.isEmpty()) {
            for (Map.Entry<String, String> entry : headers.entrySet()) {
                builder.addHeader(entry.getKey(), entry.getValue());
            }
        }
        Request request = builder.get().build();
        String result = null;
        try {
            Response response = execute4File(request);
            Headers headersObj = response.headers();
            System.out.println(headersObj);
            result = execute(request);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return result;
    }

    /**
     * 同步get请求返回请求头加响应体
     *
     * @param url
     * @return
     * @throws IOException
     * @
     */
    public static JSONObject syncGetReturnHeader(String url, Map<String, String> headers) {
        JSONObject obj = new JSONObject();
        Request.Builder builder = new Request.Builder().url(url);
        if (headers != null && !headers.isEmpty()) {
            for (Map.Entry<String, String> entry : headers.entrySet()) {
                builder.addHeader(entry.getKey(), entry.getValue());
            }
        }
        Request request = builder.get().build();
        String result = null;
        try {
            Response response = execute4File(request);
            Headers headersObj = response.headers();
            result = execute(request);
            obj.put("header", JSONObject.parseObject(JSONObject.toJSONString(headersObj)));
            obj.put("body", result);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return obj;
    }

    /**
     * 同步get请求
     *
     * @param url
     * @return
     * @throws IOException
     * @
     */
    public static String syncGet(String url, Map<String, Object> param, Map<String, String> headers) throws IOException {
        url = getGetUrl(url, param);
        Request.Builder builder = new Request.Builder();
        builder.url(url);
        if (headers != null && !headers.isEmpty()) {
            for (Map.Entry<String, String> entry : headers.entrySet()) {
                builder.addHeader(entry.getKey(), entry.getValue());
            }
        }

        Request request = builder.get().build();
        return execute(request);
    }

    /**
     * 异步get请求
     *
     * @param url
     * @param callBack
     * @
     */
    public static void asyncGet(String url, Callback callBack) {
        Request request = new Request.Builder().url(url).get().build();
        enqueue(request, callBack);
    }

    public static String syncPostForm(String url, Map<String, String> params) throws IOException {
        Request.Builder requestBuilder = new Request.Builder().url(url);
        if (params != null && !params.isEmpty()) {
            Builder formBuilder = new Builder();
            for (Map.Entry<String, String> entry : params.entrySet()) {
                formBuilder.add(entry.getKey(), entry.getValue());
            }
            RequestBody formBody = formBuilder.build();
            requestBuilder.post(formBody);
        }
        Request request = requestBuilder.build();
        String result = execute(request);
        return result;
    }

    public static String syncPostData(String url, Map<String, ContentBody> reqParam) {
        CloseableHttpClient httpclient = HttpClients.createDefault();
        CloseableHttpResponse response = null;
        try {
            // 创建http
            HttpPost httppost = new HttpPost(url);
            //setConnectTimeout：设置连接超时时间，单位毫秒。setConnectionRequestTimeout：设置从connect Manager获取Connection 超时时间，单位毫秒。
            RequestConfig defaultRequestConfig = RequestConfig.custom().setConnectTimeout(Timeout.ofDays(5000)).setConnectionRequestTimeout(Timeout.ofDays(5000)).build();
            httppost.setConfig(defaultRequestConfig);
            MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder.create();
            for (Map.Entry<String, ContentBody> param : reqParam.entrySet()) {
                multipartEntityBuilder.addPart(param.getKey(), param.getValue());
            }
            HttpEntity reqEntity = multipartEntityBuilder.build();
            httppost.setEntity(reqEntity);
            // 执行post请求.
            response = httpclient.execute(httppost);
            // 获取响应实体
            HttpEntity entity = response.getEntity();
            if (ObjectUtils.isNotEmpty(entity)) {
                return EntityUtils.toString(entity, StandardCharsets.UTF_8);
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            // 关闭连接,释放资源
            try {
                response.close();
                httpclient.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    /**
     * 发送 http post 请求，参数以原生字符串进行提交
     *
     * @param url
     * @param encode
     * @return
     */
    public static String httpPostRaw(String url, String stringJson, Map<String, String> headers, String encode) throws IOException {
        if (encode == null) {
            encode = "utf-8";
        }
        Request.Builder requestBuilder = new Request.Builder().url(url);
        if (headers != null && !headers.isEmpty()) {
            for (Map.Entry<String, String> entry : headers.entrySet()) {
                requestBuilder.addHeader(entry.getKey(), entry.getValue());
            }
        }
        if (StringUtils.isNotBlank(stringJson)) {
            requestBuilder.post(RequestBody.create(MEDIA_TYPE_JSON, stringJson));
        } else {
            requestBuilder.post(RequestBody.create(null, ""));
        }

        Request request = requestBuilder.build();
        return execute(request);

    }


    /**
     * post对象
     *
     * @param url
     * @param postBody
     * @return
     * @throws IOException
     * @
     */
    public static String syncPostJson(String url, String postBody) throws IOException {
        Request request = new Request.Builder().url(url).addHeader("Content-type", CONTENT_TYPE).addHeader("Accept", ACCEPT)
                .post(RequestBody.create(MEDIA_TYPE_JSON, postBody)).build();
        String execute = execute(request);
        return execute;
    }

    public static String syncPostJson(String url, String postBody, Map<String, String> headersMap) throws IOException {
        Headers headers = new Headers.Builder().build();
        if (ObjectUtils.isNotEmpty(headersMap)) {
            Headers.Builder builder = new Headers.Builder();
            headersMap.forEach((key, value) -> {
                builder.add(key, value);
            });
            headers = builder.build();
        }
        Request request = new Request.Builder().url(url).addHeader("Content-type", CONTENT_TYPE).addHeader("Accept", ACCEPT).headers(headers)
                .post(RequestBody.create(MEDIA_TYPE_JSON, postBody)).build();
        String execute = execute(request);
        return execute;
    }

    /**
     * put对象
     *
     * @param url
     * @param json
     * @return
     * @throws IOException
     * @
     */
    public static String syncPutJson(String url, String json) throws IOException {
        Request request = new Request.Builder().url(url).addHeader("Content-type", CONTENT_TYPE).addHeader("Accept", ACCEPT)
                .put(RequestBody.create(MEDIA_TYPE_JSON, json)).build();
        String result = execute(request);
        return result;
    }


    public static String syncPutJsonWithHeader(String url, Map<String, String> headers, String json) throws IOException {

        Request.Builder requestBuilder = new Request.Builder().url(url);
        if (headers != null && !headers.isEmpty()) {
            for (Map.Entry<String, String> entry : headers.entrySet()) {
                requestBuilder.addHeader(entry.getKey(), entry.getValue());
            }
        }
        requestBuilder.put(RequestBody.create(MEDIA_TYPE_JSON, json));
        Request request = requestBuilder.build();
        String result = execute(request);
        return result;
    }


    /**
     * delete对象
     *
     * @param url
     * @return
     * @throws IOException
     * @
     */
    public static String syncDelete(String url) throws IOException {
        Request request = new Request.Builder().url(url).delete().build();
        String result = execute(request);
        return result;
    }


    public static String syncDeleteWithHeaders(String url, Map<String, String> header) throws IOException {
        Request.Builder requestBuilder = new Request.Builder().url(url);
        if (header != null && !header.isEmpty()) {
            for (Map.Entry<String, String> entry : header.entrySet()) {
                requestBuilder.addHeader(entry.getKey(), entry.getValue());
            }
        }
        Request request = requestBuilder.delete().build();
        String result = execute(request);
        return result;
    }


    /**
     * 文件上传
     *
     * @param url   服务端路径
     * @param files 文件列表
     * @return
     * @throws IOException
     * @
     */
    public static String syncUploadFile(String url, File[] files) throws IOException {
        MultipartBody.Builder formBuilder = null;
        String result = "";
        for (File file : files) {
            RequestBody fileBody = RequestBody.create(MEDIA_TYPE_FILE, file);
            formBuilder = new MultipartBody.Builder().setType(MultipartBody.FORM);
            String fileName = file.getName();
            formBuilder.addFormDataPart("file", fileName, fileBody);
        }
        if (formBuilder != null) {
            Request request = new Request.Builder().url(url).post(formBuilder.build()).build();
            result = execute(request);
        }
        return result;
    }

    /**
     * 单文件上传
     *
     * @param url  服务端路径
     * @param file 文件列表
     * @return
     * @throws IOException
     * @
     */
    public static String syncUploadFileOne(String url, File file, JSONObject body) throws IOException {
        MultipartBody.Builder formBuilder = null;
        String result = "";
        RequestBody fileBody = RequestBody.create(MEDIA_TYPE_FILE, file);
        formBuilder = new MultipartBody.Builder().setType(MultipartBody.FORM);
        String fileName = file.getName();
        formBuilder.addFormDataPart("file", fileName, fileBody);
        Set<String> keySet = body.keySet();
        for (String key : keySet) {
            formBuilder.addFormDataPart(key, body.getString(key));
        }
        if (formBuilder != null) {
            Request request = new Request.Builder().url(url).post(formBuilder.build()).build();
            result = execute(request);
        }
        return result;
    }

    public static String syncUploadFileOneV1(String url, File file, JSONObject body) throws IOException {

        RequestBody fileBody = RequestBody.create(MEDIA_TYPE_FILE, file);
        MultipartBody.Builder formDataPart = new MultipartBody.Builder()
                .setType(MultipartBody.FORM)
                .addFormDataPart("file", file.getName(), fileBody);
        Set<String> keySet = body.keySet();
        for (String key : keySet) {
            formDataPart.addFormDataPart(key, body.getString(key));
        }
        RequestBody requestBody = formDataPart.build();

        Request request = new Request.Builder()
                .url(url)
                .post(requestBody)
                .build();

        Response response;
        response = client.newCall(request).execute();
        String jsonString = response.body().string();
        return jsonString;
    }

    /**
     * 文件下载
     *
     * @param url  服务端路径
     * @param path 本地保存位置
     * @return File 保存后的文件对象
     * @throws IOException
     * @
     */
    public static File syncDownloadFile(String url, String path) throws IOException {
        Request request = new Request.Builder().url(url).build();
        Response File = execute4File(request);
        String disposition = File.header("Content-disposition");
        String fileName = StringUtils.substringAfter(disposition, "attachment;filename=");
        File file = new File(path + fileName);
        // FileUtils.copyInputStreamToFile(File.body().byteStream(), file);
        FileOutputStream fos = null;
        try {
            InputStream is = File.body().byteStream();
            fos = new FileOutputStream(file);
            if (fos != null) {
                byte[] b = new byte[1024];
                while ((is.read(b)) != -1) {
                    fos.write(b);
                }
            }
        } catch (IOException e) {
            throw e;
        } finally {
            if (fos != null) {
                fos.close();
            }
        }
        return file;
    }

    public static String syncPostFormWithHeaders(String url, Map<String, String> params, Map<String, String> headerParams) {
        Request.Builder requestBuilder = new Request.Builder().url(url);
        if (headerParams != null && !headerParams.isEmpty()) {
            for (Map.Entry<String, String> entry : headerParams.entrySet()) {
                requestBuilder.addHeader(entry.getKey(), entry.getValue());
            }
        }
        if (params != null && !params.isEmpty()) {
            Builder formBuilder = new Builder();
            for (Map.Entry<String, String> entry : params.entrySet()) {
                formBuilder.add(String.valueOf(entry.getKey()), String.valueOf(entry.getValue()));
            }
            RequestBody formBody = formBuilder.build();
            requestBuilder.post(formBody);
        } else {
            Builder formBuilder = new Builder();
            RequestBody formBody = formBuilder.build();
            requestBuilder.post(formBody);
        }
        Request request = requestBuilder.build();
        String result = null;
        try {
            result = execute(request);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return result;
    }

    /**
     * 把流转成字符串 666版
     */
    private static String byte2String(byte[] bytes) {
        return new String(bytes);
    }

    // 同步调用
    private static String execute(Request request) throws IOException {
        Response response = client.newCall(request).execute();
        return new String(response.body().bytes(), "UTF-8");
    }

    private static Response execute4File(Request request) throws IOException {
        return client.newCall(request).execute();
    }

    // 异步调用
    private static void enqueue(Request request, Callback call) {
        client.newCall(request).enqueue(call);
    }

    public static String getGetUrl(String url, Map<String, Object> param) {
        StringBuilder getUrl = new StringBuilder(url);
        getUrl.append("?");
        Set<String> keySet = param.keySet();
        for (String key : keySet) {
            Object value = param.get(key);
            if (ObjectUtils.isNotEmpty(value)) {
                getUrl.append(key).append("=").append(value).append("&");
            }
        }

        return getUrl.substring(0, getUrl.length() - 1);
    }

    /**
     * 万能请求
     */
    public static String httpRequest(String url, JSONObject params, JSONObject headers, String method) throws IOException {
        if (StringUtils.isEmpty(method)) {
            method = "GET";
        }
        Map<String, String> headersMap = JSON.parseObject(headers.toJSONString(), Map.class);
        switch (method) {
            case "GET":
                return syncGet(url, params, headersMap);
            case "POST_JSON":
                return syncPostJson(url, params.toJSONString(), headersMap);
            case "POST_FORM":
                Map<String, String> paramsMap = JSON.parseObject(params.toJSONString(), Map.class);
                return syncPostForm(url, paramsMap);
            default:
                return null;
        }
    }

}
